#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/thread/thread.hpp>
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/stream.hpp>

#include "login_client.h"
#include "u_service.h"
#include "unreliable_layer.h"
#include "u_peer_manager.h"
#include "recursive_package.h"

#include "bingbang_pack.h"

#include <streambuf>
#include <string>
using namespace std;

using boost::asio::ip::udp;

volatile bool running = true;
boost::asio::io_service service;


void sendMessage(UPeer::Ptr peer)
{
	while(!peer->isDisposed())
	{
		if(peer->isEstablished())
		{
			auto pack = BingBangProcPack();
			pack.getMessage().set_seq("how is today ?");

			RecursivePackage::send(pack, peer);

			boost::this_thread::sleep(boost::posix_time::millisec(500));
		}
	}
}


void waitingForClose(UPeer::Ptr peer)
{
	while(true)
	{
		string command;
		cin >> command;
		if(command == "quit")
		{
			service.post(boost::bind(&UPeer::close, peer));
			boost::this_thread::sleep(boost::posix_time::millisec(1000));
			service.stop();
			break;
		}
	}
}



#include <boost/function.hpp>

int main(int args, char* argv[])
{
	auto port = atoi(argv[1]);

	auto udpService = UdpService::newInstance(port, service);
	boost::shared_ptr<UPeerManager> peerManager = UPeerManager::getInstace();

	auto unreliableLayer = boost::shared_ptr<UnreliableLayer>(
		new UnreliableLayer(peerManager, service));
	unreliableLayer->config(0.1, 50, 50);
	udpService->setPackageHandler(unreliableLayer);


	auto peer = peerManager->createChannelTo(udp::endpoint(
		boost::asio::ip::address_v4::from_string("127.0.0.1"), 8080), 
		udpService);


	boost::shared_ptr<boost::thread> sendingThread;
	auto loginAttacher = new ConnectionListenerProxy();

	auto successCallback = [&sendingThread](IIoHandler::Ptr handler)
	{
		auto peer = boost::shared_dynamic_cast<UPeer, IIoHandler>(handler);
		sendingThread.reset(new boost::thread(sendMessage, peer));
	};

	loginAttacher->setEstablishedDelegate([&sendingThread, &successCallback](UPeer::Ptr peer)
	{
		auto loginClient = new LoginClient("user", "everyone");
		loginClient->setLoginSuccessDelegate(successCallback);

		peer->setAttachment(IIoAttachment::Ptr(loginClient));
	});
	peerManager->setConnectionListener(IConnectionListener::Ptr(loginAttacher));


	boost::thread waitingForCloseThread(boost::bind(waitingForClose, peer));
	

	udpService->start();
	service.run();

	return 0;
}
